'''
pyrwi.gui - GUI widgets and helper functions for making the user 
            interface easier to build.
'''
import fltk as fltk
import time

class Fl_Log(fltk.Fl_Text_Display) :
    '''Logging widget to allow easy message logging and display.'''
    
    def __init__(self, x, y, w, h, l) :
        fltk.Fl_Text_Display.__init__(self, x, y, w, h, l)
        
        #Create a buffer for the text
        self.my_buffer = fltk.Fl_Text_Buffer()
        self.buffer( self.my_buffer )

    #Logging level access methods
    def note(self, *txt) : return self.__add('NOTE', *txt)
    def warn(self, *txt) : return self.__add('WARN', *txt)
    def error(self, *txt) : return self.__add('ERR', *txt)
    
    def __add(self, type, *txt) :
        '''Add a message to the log.
        @param type: Type of message (NOTE, WARN, ERR)
        @param txt: Text of message
        @return none'''
        new_str = type.upper() + ": " + "".join(map(str,txt)) + "\n"

        self.insert(new_str)
        return new_str

    def save(self, path) :
        self.my_buffer.savefile(path)

class Fl_Button_Stack :
    '''
    This helper function makes it easier to line up a series of buttons.
    '''
    Types = {"std":fltk.Fl_Button, "light":fltk.Fl_Light_Button,
             "check":fltk.Fl_Check_Button, "round":fltk.Fl_Round_Button,
             "repeat":fltk.Fl_Repeat_Button, "return":fltk.Fl_Return_Button, 
             "toggle":fltk.Fl_Toggle_Button, }
    '''Types of buttons that can be used in the stack.'''
    
    def __init__(self, x,y,w,h,label="",pdx=0,pdy=0,default_type="std") :
        '''
        Constructor
        @param x,y: Top left corner of button stack
        @param w,h: Dimensions of a single button
        @param label: OPTIONAL Label for the stack of buttons
        @param pdx,pdy: OPTIONAL Percent of button width/height to space
                        each new button. This controls vert vs. horz.
                        stack. Only one of these should be non-zero
        @param default_type: OPTIONAL Default type to use for each new 
                        button
        '''
        self.x0=x
        self.y0=y
        self.button_w=w
        self.button_h=h
        self.label=label #TODO: Implement
        
        #Steps to take in stack
        self.dx = 0
        self.dy = 0
        if pdx != 0 : self.dx=int(self.button_w*(1+pdx))
        if pdy != 0 : self.dy=int(self.button_h*(1+pdy))
        
        #Default callback function
        try :
            self.default_btn_func = self.Types[default_type]
        except KeyError, e:
            raise KeyError("'%s' is not a valid button type." % default_type)
        
        #Put the first button on the x0,y0 corner
        self.cur_x = self.x0
        self.cur_y = self.y0
        
    def addBtn(self,label, cb_func, type=None) :
        '''
        Add a button to the stack.
        @param label: Button label
        @param cb_func: Call back function to handle button press
        @param type: OPTIONAL specify the type of button to make
                        (See self.Types for a list of options)
        @return: New button object
        '''
        #Pick the right kind of button
        if type == None :
            btn_func = self.default_btn_func
        else :
            try :
                btn_func = self.Types[type]
            except KeyError, e:
                raise KeyError("'%s' is not a valid button type." % type)
                
        btn = btn_func(self.cur_x, self.cur_y, self.button_w, self.button_h, label)
        btn.callback(cb_func)
        self.updateCoords()
        return btn
    
    def updateCoords(self) :
        self.cur_x += self.dx
        self.cur_y += self.dy

#TODO: Change examples to use this
class RWI_Window(fltk.Fl_Window):
    '''
    RWI_Window - An FLTK Window that provides the following
    - An Idler function that allows DAQmx to raise callbacks
    - A default menu
    - A default log window
    '''
    
    def __init__(self, w, h, label, menu=None):
        '''
        Build a new RWI_Window
        @param w,h: Width and height
        @param label: Window title
        @param menu: Override the default window
        '''
        #Call the parent constructor
        fltk.Fl_Window.__init__(self, w, h, label)
        
        #--- Menus
        if menu is None :
            menuitems = (( "&File",              0, 0, 0, fltk.FL_SUBMENU ),
                ( "E&xit", fltk.FL_CTRL + ord('q'), self.mnu_quit_cb ),
                ( None, 0 ),

              ( "&Log", 0, 0, 0, fltk.FL_SUBMENU ),
                ( "&Save Log", fltk.FL_CTRL + ord('s'), self.mnu_log_save ),
                ( None, 0 ),

              ( None, 0 )
            )
        else :
            menuitems = menu
        m = fltk.Fl_Menu_Bar(0, 0, w, 30)
        m.copy(menuitems)
        
        #--- Log
        log_h = 150
        self.log = Fl_Log(0, h-log_h, w, log_h, "Label")
        #TODO: Add a re-sizer handle to the log
        self.logFile_chooser = fltk.Fl_File_Chooser(".", "*.{txt,csv}", 
            fltk.Fl_File_Chooser.CREATE, "Log file name")
        
        #The idler allows DAQmx to raise calls
        fltk.Fl.add_idle(self.idler)
    
    def idler(self, ptr) :
        '''This gives DAQmx a chance to raise an event'''
        time.sleep(0.1)

    #
    # Event Call Backs
    #
    def mnu_quit_cb(self, event) : self.hide()
    
    def mnu_log_save(self, event) :
        '''
        Default log file saving routine. self.logFile_chooser or
        this function can be overridden to customize this behavior
        '''
        self.logFile_chooser.show()
        fname = self.logFile_chooser.value()
        if fname :
            self.log.save(fname)
